"""
上传命令 - 一键创建 Issue 和 PR
"""
import click
import re
import sys
from pathlib import Path
from typing import Optional
from gitcode_client import GitCodeClient
from config import Config
from repo_manager import RepoManager
from template_loader import TemplateLoader, get_commit_first_line
from i18n import t

SUMMARY_PLACEHOLDERS = [
    "请描述本次Pull Request的变更内容。",
    "Please describe the changes in this Pull Request."
]
SUMMARY_SECTION_HEADERS = [
    "## 变更摘要（必填）",
    "## Summary (Required)",
]
NON_INTERACTIVE_SECTION_HEADERS = SUMMARY_SECTION_HEADERS + [
    "## 关联 Issue（必填）",
    "## Related Issues (Required)",
]
CHECKBOX_PATTERN = re.compile(r"^(\s*)- \[( |x|X)\] (.+)$")
TEMPLATE_PROMPT_CACHE = {}
TEMPLATE_PROMPT_DECISION = None
# 是否启用交互式模板填写（通过 CLI 开关控制，默认关闭）
TEMPLATE_PROMPT_ENABLED = False


def _is_interactive_terminal() -> bool:
    """
    判断当前是否在交互式终端中运行。
    当 stdout/stderr 不是 TTY 时，click.confirm 会在等待输入时抛错。
    """
    try:
        return sys.stdin.isatty() and sys.stdout.isatty()
    except Exception:
        return False


def _should_prompt_template_inputs() -> bool:
    """
    统一控制是否对模板进行交互式填写（复选框、二级标题等）。
    """
    if not TEMPLATE_PROMPT_ENABLED:
        return False
    if not _is_interactive_terminal():
        return False

    global TEMPLATE_PROMPT_DECISION
    if TEMPLATE_PROMPT_DECISION is None:
        prompt_message = t("upload.checkbox_prompt_opt_in")
        if prompt_message == "upload.checkbox_prompt_opt_in":
            prompt_message = "检测到 PR 模板包含复选框或需填写的章节，是否现在填写？"
        TEMPLATE_PROMPT_DECISION = click.confirm(prompt_message, default=True)

    return TEMPLATE_PROMPT_DECISION


def _prompt_template_checkboxes(template: str) -> str:
    """
    解析模板中的 checkbox 选项，并通过 click 提示用户勾选。
    同时支持对二级标题内容进行填写。
    相同模板（包括 Issue URL 等内容）仅提示一次，复用填写结果。
    """
    if not template:
        return template
    if not _is_interactive_terminal():
        return template

    if not _should_prompt_template_inputs():
        TEMPLATE_PROMPT_CACHE[template] = template
        return template

    cached = TEMPLATE_PROMPT_CACHE.get(template)
    if cached is not None:
        return cached

    lines = template.splitlines()
    section = None
    subsection = None
    in_code_block = False
    changed = False

    for idx, line in enumerate(lines):
        stripped = line.strip()
        if stripped.startswith("```"):
            in_code_block = not in_code_block
            continue
        if in_code_block:
            continue
        if stripped.startswith("## "):
            section = stripped.lstrip("#").strip()
            subsection = None
            continue
        if stripped.startswith("### "):
            subsection = stripped.lstrip("#").strip()
            continue

        match = CHECKBOX_PATTERN.match(line)
        if not match:
            continue

        indent, marker, label = match.groups()
        default_checked = marker.lower() == "x"
        context_parts = [part for part in (section, subsection) if part]
        prompt_prefix = " / ".join(context_parts) if context_parts else "Checklist"
        question = f"{prompt_prefix} - {label}"

        should_check = click.confirm(question, default=default_checked)
        new_marker = "x" if should_check else " "
        if (new_marker == "x") != default_checked:
            changed = True
        lines[idx] = f"{indent}- [{new_marker}] {label}"

    result = "\n".join(lines)
    result, section_changed = _prompt_section_bodies(result)
    changed = changed or section_changed
    TEMPLATE_PROMPT_CACHE[template] = result if changed else template
    return result


def _prompt_section_bodies(template: str) -> tuple:
    """
    遍历模板中的二级标题（除特定自动处理的标题），询问用户是否需要填写对应内容。
    返回 (新的模板内容, 是否有修改)。
    """
    if not template:
        return template, False
    if not _should_prompt_template_inputs():
        return template, False

    lines = template.splitlines()
    idx = 0
    changed = False

    while idx < len(lines):
        line = lines[idx]
        stripped = line.strip()

        if not stripped.startswith("## "):
            idx += 1
            continue

        if stripped in NON_INTERACTIVE_SECTION_HEADERS:
            idx += 1
            continue

        start = idx + 1
        end = start
        while end < len(lines):
            next_line = lines[end].strip()
            if next_line.startswith("## "):
                break
            end += 1

        section_lines = lines[start:end]

        if _section_has_only_checkboxes(section_lines):
            idx = end
            continue
        existing_content = "\n".join(section_lines).strip()
        new_content = _prompt_section_input(stripped, existing_content)

        if new_content is None:
            idx = end
            continue

        replacement = _build_section_replacement(section_lines, new_content)
        lines = lines[:start] + replacement + lines[end:]
        idx = start + len(replacement)
        changed = True

    return "\n".join(lines), changed


def _prompt_section_input(title: str, current: str) -> Optional[str]:
    """
    交互式地获取二级标题的文本内容。
    返回新内容字符串；返回 None 表示保持原样。
    """
    if not _should_prompt_template_inputs():
        return None

    click.echo("\n" + "-" * 50)
    click.echo(t("upload.section_prompt_title", title=title))

    if current:
        click.echo(t("upload.section_prompt_current"))
        for line in current.splitlines():
            click.echo(f"    {line}")

    click.echo(t("upload.section_prompt_hint"))

    lines = []
    has_input = False
    cleared = False

    while True:
        try:
            user_input = input("> ")
        except EOFError:
            break
        if user_input == "" and not has_input:
            # 保留原内容
            return None
        if user_input.strip() == ".":
            cleared = not has_input
            break
        lines.append(user_input)
        has_input = True

    if not has_input and cleared:
        return ""

    if not has_input:
        return None

    return "\n".join(lines).rstrip()


def _build_section_replacement(section_lines: list, new_content: str) -> list:
    """
    根据原 section 的空行分布，生成新的内容块。
    """
    leading = 0
    trailing = 0

    while leading < len(section_lines) and not section_lines[leading].strip():
        leading += 1

    while trailing < len(section_lines) - leading and not section_lines[-1 - trailing].strip():
        trailing += 1

    replacement = []

    if new_content:
        if leading == 0:
            leading = 1
        replacement.extend([""] * leading)
        replacement.extend(new_content.splitlines())
    else:
        # 用户清空内容时，保留原始的前置空行（如果有）
        replacement.extend([""] * leading)

    replacement.extend([""] * trailing)
    return replacement


def _section_has_only_checkboxes(section_lines: list) -> bool:
    """
    如果段落中只有复选框（或空行），则返回 True。
    这类段落不需要额外的文本输入。
    """
    for line in section_lines:
        stripped = line.strip()
        if not stripped:
            continue
        if not CHECKBOX_PATTERN.match(line):
            return False
    return True
def _remove_signed_off(body: str) -> str:
    if not body:
        return body
    lines = [
        line for line in body.splitlines()
        if not line.strip().lower().startswith("signed-off-by:")
    ]
    return "\n".join(lines).strip()


def _replace_summary_section(pr_body: str, commits_content: str) -> tuple:
    """
    查找特定标题（如“## 变更摘要（必填）”），并将该标题到下一个标题之间的内容替换为 commits 概览。
    保留原本的换行数量，以免破坏模板布局。
    """
    if not pr_body:
        return pr_body, False

    lines = pr_body.splitlines()
    for idx, line in enumerate(lines):
        if line.strip() not in SUMMARY_SECTION_HEADERS:
            continue

        start = idx + 1
        end = start
        while end < len(lines):
            next_line = lines[end].strip()
            if next_line.startswith("## ") and end != idx:
                break
            end += 1

        section_lines = lines[start:end]
        leading_blanks = 0
        while leading_blanks < len(section_lines) and not section_lines[leading_blanks].strip():
            leading_blanks += 1

        trailing_blanks = 0
        while trailing_blanks < len(section_lines) - leading_blanks and not section_lines[-1 - trailing_blanks].strip():
            trailing_blanks += 1

        prefix_blanks = max(1, leading_blanks) if commits_content else leading_blanks
        new_section = []
        new_section.extend([""] * prefix_blanks)
        if commits_content:
            new_section.extend(commits_content.splitlines())
        new_section.extend([""] * trailing_blanks)

        # 防止多余空行：如果原 section 没有内容也没有前置空行，确保至少存在一个空行
        if not section_lines and not new_section:
            new_section = [""]

        updated_lines = lines[:start] + new_section + lines[end:]
        return "\n".join(updated_lines).strip(), True

    return pr_body, False


def _inject_commit_summary(pr_body: str, commits_content: str) -> str:
    if not commits_content:
        return pr_body

    # 先尝试替换特定标题段落（如“## 变更摘要（必填）”）
    pr_body, section_replaced = _replace_summary_section(pr_body, commits_content)
    if section_replaced:
        return pr_body

    summary_block = f"## Changes Summary\n\n{commits_content}"

    if pr_body:
        replaced = False
        new_lines = []
        for line in pr_body.splitlines():
            stripped = line.strip()
            if stripped in SUMMARY_PLACEHOLDERS:
                if not replaced:
                    new_lines.append(commits_content)
                    replaced = True
                # skip additional placeholders entirely
                continue
            new_lines.append(line)

        if replaced:
            return "\n".join(new_lines).strip()

        return f"{summary_block}\n\n---\n\n{pr_body}"

    return summary_block


def _read_template_content(repo_path: Path, template_path: str) -> str:
    if not template_path:
        return None
    try:
        full_path = Path(repo_path) / template_path
        with open(full_path, "r", encoding="utf-8") as f:
            return f.read()
    except Exception:
        return None


def parse_issue_spec(issue_spec: str) -> tuple:
    """
    解析 Issue 规格字符串
    格式: repo#issue_number
    例如: cangjie_compiler#123
    
    :param issue_spec: Issue 规格字符串
    :return: (repo, issue_number) 或 (None, None) 如果格式错误
    """
    if '#' not in issue_spec:
        return None, None
    
    repo, issue_str = issue_spec.rsplit('#', 1)
    repo = repo.strip()
    try:
        issue_number = int(issue_str.strip())
        return repo, issue_number
    except ValueError:
        return None, None


def get_changed_repos(repo_manager: RepoManager) -> list:
    """
    获取有更新的仓库列表（只检查已初始化的仓库）
    包括：有未提交更改、当前分支相对于 base 分支有新的提交
    """
    changed_repos = []
    repos = repo_manager.get_initialized_repos()  # 只检查已初始化的仓库
    config = repo_manager.config
    
    # 获取 base 分支（PR 目标分支）
    base_branch = config.get_base_branch()
    
    for repo in repos:
        repo_path = repo_manager.get_repo_path(repo)
        if not repo_path.exists():
            continue
        
        try:
            import subprocess
            
            # 检查是否有未提交的更改
            status_result = subprocess.run(
                ["git", "status", "--porcelain"],
                cwd=repo_path,
                capture_output=True,
                text=True
            )
            has_changes = bool(status_result.stdout.strip())
            
            if has_changes:
                changed_repos.append(repo)
                continue
            
            # 获取当前分支
            branch_result = subprocess.run(
                ["git", "rev-parse", "--abbrev-ref", "HEAD"],
                cwd=repo_path,
                capture_output=True,
                text=True
            )
            if branch_result.returncode != 0:
                continue
            
            current_branch = branch_result.stdout.strip()
            
            # 确保 upstream remote 存在
            repo_manager._ensure_upstream_remote(repo_path, repo)
            
            # 从 upstream 的 base 分支 fetch 最新信息
            subprocess.run(
                ["git", "fetch", "upstream", base_branch],
                cwd=repo_path,
                capture_output=True,
                text=True
            )
            
            # 检查当前分支相对于 base 分支是否有新的提交
            # 统一使用 upstream/{base_branch} 作为基准，因为本地 base_branch 可能不存在
            # 如果当前分支就是 base 分支，则检查相对于 upstream/base_branch 的领先
            if current_branch == base_branch:
                ahead_result = subprocess.run(
                    ["git", "rev-list", "--count", f"upstream/{base_branch}..HEAD"],
                    cwd=repo_path,
                    capture_output=True,
                    text=True
                )
            else:
                # 当前分支不是 base 分支，检查相对于 upstream/base_branch 的新提交
                # 使用 upstream/{base_branch} 而不是本地 base_branch，因为本地分支可能不存在
                ahead_result = subprocess.run(
                    ["git", "rev-list", "--count", f"upstream/{base_branch}..HEAD"],
                    cwd=repo_path,
                    capture_output=True,
                    text=True
                )
            
            if ahead_result.returncode == 0:
                ahead_count = int(ahead_result.stdout.strip() or "0")
                if ahead_count > 0:
                    changed_repos.append(repo)
        except Exception as e:
            # 忽略错误，继续检查下一个仓库
            pass
    
    return changed_repos


def get_commits_content(repo_path, base_branch, current_branch):
    """
    获取当前分支相对于 base 分支的所有 commits 内容
    :param repo_path: 仓库路径
    :param base_branch: base 分支名
    :param current_branch: 当前分支名
    :return: commits 内容字符串，格式为每个 commit 一行
    """
    import subprocess
    
    try:
        # 统一使用 upstream/{base_branch} 作为基准，因为本地 base_branch 可能不存在
        # 先确保 upstream remote 存在（如果不存在，尝试添加）
        # 检查 upstream remote 是否存在
        remote_check = subprocess.run(
            ["git", "remote", "get-url", "upstream"],
            cwd=repo_path,
            capture_output=True,
            text=True
        )
        
        # 如果 upstream 不存在，尝试添加（需要从 config 获取主仓 URL）
        if remote_check.returncode != 0:
            # 尝试从 config 获取仓库信息并添加 upstream
            try:
                config = Config()
                repo_name = Path(repo_path).name
                upstream_url = config.get_repo_url(repo_name, None)  # None 表示主仓
                subprocess.run(
                    ["git", "remote", "add", "upstream", upstream_url],
                    cwd=repo_path,
                    capture_output=True,
                    text=True
                )
            except Exception:
                pass  # 如果添加失败，继续尝试 fetch（可能已经存在）
        
        # Fetch upstream 的 base_branch
        subprocess.run(
            ["git", "fetch", "upstream", base_branch],
            cwd=repo_path,
            capture_output=True,
            text=True
        )
        
        # 统一使用 upstream/{base_branch}..HEAD，无论当前分支是否是 base_branch
        range_spec = f"upstream/{base_branch}..HEAD"
        
        # 获取所有 commits 的完整信息（包括标题和描述）
        result = subprocess.run(
            ["git", "log", range_spec, "--pretty=format:- %s%n  %b", "--reverse"],
            cwd=repo_path,
            capture_output=True,
            text=True
        )
        
        if result.returncode == 0 and result.stdout.strip():
            # 清理输出，移除多余的空行
            commits_content = result.stdout.strip()
            # 将多个连续空行替换为单个空行
            import re
            commits_content = re.sub(r'\n{3,}', '\n\n', commits_content)
            return commits_content
        
        return None
    except Exception:
        return None


def push_branch_to_fork(repo, repo_manager, config, branch_name):
    """
    将分支推送到 fork 仓（origin），如果远程分支不存在则创建，如果存在则强制推送
    :param repo: 仓库名
    :param repo_manager: 仓库管理器
    :param config: 配置对象
    :param branch_name: 分支名
    :return: (是否成功, 错误信息)
    """
    repo_path = repo_manager.get_repo_path(repo)
    if not repo_path.exists():
        return False, 'Repository not cloned locally'
    
    try:
        import subprocess
        
        # 确保 origin remote 存在
        remote_result = subprocess.run(
            ["git", "remote", "get-url", "origin"],
            cwd=repo_path,
            capture_output=True,
            text=True
        )
        if remote_result.returncode != 0:
            return False, 'Origin remote not found'
        
        # 强制推送到 origin（无论远程分支是否存在，都强制推送以确保远程分支存在）
        push_result = subprocess.run(
            ["git", "push", "--force", "origin", f"{branch_name}:{branch_name}"],
            cwd=repo_path,
            capture_output=True,
            text=True
        )
        
        if push_result.returncode == 0:
            return True, None
        else:
            error_msg = push_result.stderr.strip() or push_result.stdout.strip()
            return False, error_msg
    except Exception as e:
        return False, str(e)


def collect_pr_info(
    repo,
    repo_manager,
    config,
    owner,
    base,
    issue_url_final,
    prompt_checkboxes=True,
):
    """
    收集单个仓库的 PR 信息（用于预览和创建）
    :return: PR 信息字典，如果出错则包含 'error' 键
    """
    repo_path = repo_manager.get_repo_path(repo)
    if not repo_path.exists():
        return {'repo': repo, 'error': 'Repository not cloned locally'}
    
    try:
        import subprocess
        
        # 获取当前分支名
        branch_result = subprocess.run(
            ["git", "rev-parse", "--abbrev-ref", "HEAD"],
            cwd=repo_path,
            capture_output=True,
            text=True
        )
        if branch_result.returncode != 0:
            return {'repo': repo, 'error': 'Cannot get current branch'}
        
        branch_name = branch_result.stdout.strip()
        
        # 构建 head（PR 需要格式：username:branch_name）
        username = config.get_gitcode_username()
        
        if not username:
            # 尝试从 remote 获取用户名
            remote_result = subprocess.run(
                ["git", "remote", "get-url", "origin"],
                cwd=repo_path,
                capture_output=True,
                text=True
            )
            if remote_result.returncode == 0:
                remote_url = remote_result.stdout.strip()
                if 'gitcode.com' in remote_url:
                    parts = remote_url.replace('.git', '').rstrip('/').split('/')
                    try:
                        gitcode_idx = [i for i, p in enumerate(parts) if 'gitcode.com' in p][0]
                        if len(parts) > gitcode_idx + 1:
                            potential_username = parts[gitcode_idx + 1]
                            if potential_username.lower() != config.get_base_org().lower():
                                username = potential_username
                    except (IndexError, ValueError):
                        pass
        
        if not username:
            return {'repo': repo, 'error': 'Cannot determine username'}
        
        head = f"{username}:{branch_name}"
        
        # 确定 PR 的目标分支（base）
        # 优先级：命令行参数 > 配置中的 base_branch > 当前分支
        pr_base = base if base else config.get_base_branch()
        
        # 获取 PR 标题
        pr_title = get_commit_first_line(repo_path, branch_name)
        if not pr_title:
            pr_title = f"Update {repo}"
        
        # 获取 commits 内容（相对于 base 分支）
        commits_content = get_commits_content(repo_path, pr_base, branch_name)
        
        # 加载 PR 模板
        loader = TemplateLoader(repo_path, language=config.get_language())
        pr_body = ""
        template = loader.get_pr_template()
        if template:
            pr_body = (
                _prompt_template_checkboxes(template)
                if prompt_checkboxes
                else template
            )
            if issue_url_final:
                pr_body = loader.append_issue_url(pr_body, issue_url_final)
        else:
            if issue_url_final:
                pr_body = f"Related Issue: {issue_url_final}"
            else:
                pr_body = ""
        
        pr_body = _inject_commit_summary(pr_body, commits_content)
        pr_body = _remove_signed_off(pr_body)
        
        return {
            'repo': repo,
            'title': pr_title,
            'head': head,
            'base': pr_base,
            'body': pr_body,
            'branch': branch_name,
            'username': username
        }
    except Exception as e:
        return {'repo': repo, 'error': str(e)}


def preview_issue_and_prs(owner, issue_repo, issue_title, issue_template_path, issue_url, 
                          changed_repos, repo_manager, config, base):
    """
    预览即将创建的 Issue 和 PR 内容
    """
    click.echo("\n" + "="*70)
    click.echo(t("upload.preview_mode_issue_and_pr_content"))
    click.echo("="*70)
    
    # 预览 Issue
    click.echo(t("upload.n_issue"))
    click.echo("-" * 70)
    if issue_url:
        click.echo(t("upload.using_existing_issue_url", url=issue_url))
    elif issue_repo:
        click.echo(t("upload.repository_owner_repo", owner=owner, repo=issue_repo))
        click.echo(t("upload.title_title", title=issue_title))
        if issue_template_path:
            repo_path = repo_manager.get_repo_path(issue_repo)
            click.echo(t("upload.template_path_path", path=issue_template_path))
            if repo_path.exists():
                template_content = _read_template_content(repo_path, issue_template_path)
                if template_content:
                    click.echo(t("upload.template_content"))
                    for line in template_content.splitlines():
                        click.echo(f"    {line}")
                else:
                    click.echo(t("upload.unable_to_read_template_content"))
            else:
                click.echo(t("upload.repository_path_not_found"))
        else:
            click.echo(t("upload.body_empty"))
    else:
        click.echo(t("upload.no_issue_will_be_created"))
    
    # 预览 PRs
    click.echo(t("upload.n_pull_requests_count", count=len(changed_repos)))
    click.echo("-" * 70)
    
    # 为 PR body 准备 issue_url（预览时使用占位符）
    if issue_url:
        issue_url_final = issue_url
    elif issue_repo:
        issue_url_final = f"https://gitcode.com/{owner}/{issue_repo}/issues/XXX"
    else:
        issue_url_final = None
    
    for idx, repo in enumerate(changed_repos, 1):
        pr_info = collect_pr_info(
            repo,
            repo_manager,
            config,
            owner,
            base,
            issue_url_final,
            prompt_checkboxes=False,
        )
        
        click.echo(t("upload.n_index_repository_repo", index=idx, repo=repo))
        
        if 'error' in pr_info:
            click.echo(t("upload.error_message", message=pr_info['error']), err=True)
            continue
        
        click.echo(t("upload.title_value", value=pr_info['title']))
        click.echo(t("upload.head_value", value=pr_info['head']))
        click.echo(t("upload.base_value", value=pr_info['base']))
        click.echo(t("upload.branch_value", value=pr_info['branch']))
        click.echo(t("upload.username_value", value=pr_info['username']))
        
        if pr_info['body']:
            click.echo(t("upload.body"))
            # 显示 body 内容，每行前加缩进
            body_lines = pr_info['body'].split('\n')
            for line in body_lines:
                click.echo(f"        {line}")
        else:
            click.echo(t("upload.body_empty_2"))
    
    click.echo("\n" + "="*70)
    click.echo(t("upload.end_of_preview"))
    click.echo("="*70 + "\n")


@click.command()
@click.option('--issue', '-i', help='Issue 规格，格式: repo#issue_number (例如: cangjie_compiler#123) 或 Issue URL')
@click.option('--issue-repo', '-r', help='创建 Issue 的仓库（默认使用第一个有更新的仓库，与 --issue 互斥）')
@click.option('--issue-title', '-t', help='Issue 标题（如果不提供，会使用第一个仓库的 commit 信息）')
@click.option('--base', '-b', default=None, help='PR 目标分支（默认使用配置中的 base_branch，如果未设置则使用 default_branch）')
@click.option('--yes', '-y', is_flag=True, help='跳过确认，直接创建')
@click.option('--preview', '-p', is_flag=True, help='预览模式：显示即将创建的 Issue 和 PR 内容，但不实际创建')
@click.option('--template', '-T', 'fill_template', is_flag=True, help='启用模板交互填写（默认关闭）')
def upload(issue, issue_repo, issue_title, base, yes, preview, fill_template):
    """
    检测所有有更新的仓库，创建 Issue 和 PR
    
    工作流程：
    1. 检测所有有更新的仓库
    2. 显示变更的仓库列表，等待用户确认
    3. 在指定仓库创建 Issue（或使用提供的 Issue URL）
    4. 为所有有更新的仓库创建 PR
    5. 打印所有创建的 Issue 和 PR URL
    """
    config = Config()
    client = GitCodeClient()
    repo_manager = RepoManager(config)

    # 控制模板交互填写开关
    global TEMPLATE_PROMPT_ENABLED, TEMPLATE_PROMPT_DECISION
    TEMPLATE_PROMPT_ENABLED = fill_template
    # 每次执行 upload 时重置之前的决策
    TEMPLATE_PROMPT_DECISION = None
    
    owner = config.get_base_org()
    
    # 检查选项互斥性
    if issue and issue_title:
        click.echo(t("upload.error_issue_and_issue_title_are"), err=True)
        click.echo(t("upload.use_issue_to_reference_an_existing"), err=True)
        return
    
    if issue and issue_repo:
        click.echo(t("upload.error_issue_and_issue_repo_are"), err=True)
        click.echo(t("upload.use_issue_to_reference_an_existing_2"), err=True)
        return
    
    # 处理 --issue 选项（优先级最高）
    issue_url = None
    if issue:
        # 检查是否是 URL 格式
        if issue.startswith('http://') or issue.startswith('https://'):
            issue_url = issue
            issue_repo = None
        else:
            # 解析 repo#issue_number 格式
            parsed_repo, parsed_issue_number = parse_issue_spec(issue)
            if parsed_repo and parsed_issue_number:
                # 通过 API 获取 Issue URL
                click.echo(t("upload.fetching_issue_repo_number", repo=parsed_repo, number=parsed_issue_number))
                ok, issue_info = client.get_issue(owner, parsed_repo, parsed_issue_number)
                if ok:
                    issue_url = issue_info.get('html_url') or issue_info.get('url', '')
                    if issue_url:
                        click.echo(t("upload.found_issue_url", url=issue_url))
                    else:
                        # 如果 API 没有返回 URL，手动构建
                        issue_url = f"https://gitcode.com/{owner}/{parsed_repo}/issues/{parsed_issue_number}"
                        click.echo(t("upload.using_issue_url", url=issue_url))
                else:
                    error_msg = issue_info.get('error', 'Unknown error')
                    click.echo(
                        t("upload.failed_to_fetch_issue_repo_number", repo=parsed_repo, number=parsed_issue_number, error=error_msg),
                        err=True,
                    )
                    # 即使 API 失败，也尝试使用构建的 URL
                    issue_url = f"https://gitcode.com/{owner}/{parsed_repo}/issues/{parsed_issue_number}"
                    click.echo(t("upload.using_constructed_url_url", url=issue_url))
                issue_repo = None
            else:
                click.echo(t("upload.error_invalid_issue_format_value_expected", value=issue), err=True)
                return
    
    # base 参数用于覆盖默认行为，如果指定了则所有仓库使用该分支
    # 否则使用配置中的 base_branch（在 init 时默认设置为 -b 设置的分支）
    
    # 1. 检测有更新的仓库
    click.echo(t("upload.detecting_changed_repositories"))
    changed_repos = get_changed_repos(repo_manager)
    
    if not changed_repos:
        click.echo(t("upload.no_changed_repositories_found"))
        click.echo(t("upload.repositories_with_uncommitted_changes_or_commits"))
        return
    
    click.echo(t("upload.found_count_changed_repository_ies", count=len(changed_repos)))
    for repo in changed_repos:
        click.echo(t("upload.repo", repo=repo))
    
    # 2. 确定 Issue 仓库和标题
    final_issue_repo = issue_repo
    if not final_issue_repo and not issue_url:
        if changed_repos:
            final_issue_repo = changed_repos[0]
        else:
            click.echo(t("upload.error_no_changed_repositories_and_no"), err=True)
            return
    
    # 确定 Issue 标题（如果需要创建 Issue）
    final_issue_title = None
    issue_template_path = None
    if not issue_url and final_issue_repo:
        final_issue_title = issue_title
        if not final_issue_title:
            # 尝试从第一个仓库获取 commit 信息
            repo_path = repo_manager.get_repo_path(final_issue_repo)
            if repo_path.exists():
                commit_title = get_commit_first_line(repo_path)
                if commit_title:
                    final_issue_title = commit_title
                else:
                    final_issue_title = f"Update: {', '.join(changed_repos)}"
            else:
                final_issue_title = f"Update: {', '.join(changed_repos)}"
        
        # 获取 Issue 模板路径
        repo_path = repo_manager.get_repo_path(final_issue_repo)
        if repo_path.exists():
            loader = TemplateLoader(repo_path, language=config.get_language())
            template_path = loader.get_issue_template_path()
            if template_path:
                issue_template_path = template_path
    
    # 如果是预览模式，显示预览并退出
    if preview:
        preview_issue_and_prs(owner, final_issue_repo, final_issue_title, issue_template_path, 
                              issue_url, changed_repos, repo_manager, config, base)
        return
    
    # 3. 显示将要执行的操作并等待确认
    if not yes:
        click.echo("\n" + "="*60)
        click.echo(t("upload.summary"))
        click.echo("="*60)
        
        if issue_url:
            click.echo(t("upload.issue_url_provided", url=issue_url))
        elif final_issue_repo:
            click.echo(t("upload.issue_will_be_created_in_repo", repo=final_issue_repo))
            click.echo(t("upload.title_title", title=final_issue_title))
            if issue_template_path:
                click.echo(t("upload.template_path", path=issue_template_path))
        
        click.echo(t("upload.nprs_will_be_created_for_count", count=len(changed_repos)))
        for repo in changed_repos:
            click.echo(t("upload.repo", repo=repo))
        
        confirm_key = "upload.nproceed_with_creating_issue_and_prs"
        if issue_url:
            confirm_key = "upload.nproceed_with_creating_prs"

        if not click.confirm(t(confirm_key)):
            click.echo(t("common.cancelled"))
            return
    
    # 4. 创建 Issue（如果需要）
    issue_url_final = issue_url
    issue_number = None
    
    if not issue_url_final and final_issue_repo:
        click.echo(t("upload.ncreating_issue_in_owner_repo", owner=owner, repo=final_issue_repo))
        ok, result = client.create_issue(owner, final_issue_repo, final_issue_title, template_path=issue_template_path)
        
        if ok:
            issue_number = result.get('number') or result.get('iid')
            issue_url_final = result.get('web_url') or result.get('html_url') or result.get('url', '')
            click.echo(t("upload.issue_created_url", url=issue_url_final))
        else:
            error_msg = result.get('error_message') or result.get('error') or str(result)
            click.echo(t("upload.failed_to_create_issue_error", error=error_msg), err=True)
            return
    
    if not issue_url_final:
        click.echo(t("upload.error_no_issue_url_available"), err=True)
        return
    
    # 从 issue_url 提取 issue_number（如果还没有）
    if not issue_number and issue_url_final:
        try:
            # URL 格式: https://gitcode.com/owner/repo/issues/123
            parts = issue_url_final.rstrip('/').split('/')
            issue_number = int(parts[-1])
        except Exception:
            pass
    
    # 5. 为所有有更新的仓库创建 PR
    click.echo(t("upload.ncreating_prs"))
    pr_results = []
    
    for repo in changed_repos:
        # 使用 collect_pr_info 收集 PR 信息
        pr_info = collect_pr_info(
            repo,
            repo_manager,
            config,
            owner,
            base,
            issue_url_final,
            prompt_checkboxes=True,
        )
        
        if 'error' in pr_info:
            click.echo(t("upload.skipping_repo_error", repo=repo, error=pr_info['error']))
            pr_results.append(pr_info)
            continue
        
        # 先推送分支到 fork 仓（确保远程分支存在）
        branch_name = pr_info['branch']
        click.echo(t("upload.pushing_branch_branch_to_fork_for", branch=branch_name, repo=repo))
        push_ok, push_error = push_branch_to_fork(repo, repo_manager, config, branch_name)
        
        if not push_ok:
            click.echo(t("upload.warning_failed_to_push_branch_error", error=push_error), err=True)
            click.echo(t("upload.continuing_anyway"))
        else:
            click.echo(t("upload.branch_pushed_to_fork"))
        
        click.echo(t("upload.creating_pr_for_repo_base_base", repo=repo, base=pr_info['base']))
        ok, result = client.create_pr(owner, repo, pr_info['title'], pr_info['head'], 
                                     pr_info['base'], pr_info['body'], None, None)
        
        if ok:
            pr_number = result.get('number') or result.get('iid')
            pr_url = result.get('web_url') or result.get('html_url') or result.get('url', '')
            pr_results.append({
                'repo': repo,
                'number': pr_number,
                'url': pr_url,
                'title': pr_info['title']
            })
            click.echo(t("upload.pr_number_created", number=pr_number))
        else:
            error_msg = result.get('error_message') or result.get('error') or str(result)
            click.echo(t("upload.failed_error", error=error_msg), err=True)
            pr_results.append({
                'repo': repo,
                'error': error_msg
            })
    
    # 5. 打印结果
    click.echo("\n" + "="*60)
    click.echo(t("cherry.summary"))
    click.echo("="*60)
    
    if issue_url_final:
        click.echo(t("upload.nissue"))
        click.echo(t("upload.url_url", url=issue_url_final))
        if issue_number:
            click.echo(t("upload.number_number", number=issue_number))
    
    if pr_results:
        click.echo(t("upload.npull_requests_count", count=len(pr_results)))
        for pr_info in pr_results:
            if 'error' in pr_info:
                click.echo(t("upload.repo_error", repo=pr_info['repo'], error=pr_info['error']))
            else:
                click.echo(t("upload.repo_pr_number", repo=pr_info['repo'], number=pr_info['number']))
                click.echo(t("upload.url_url_2", url=pr_info['url']))
                click.echo(t("upload.title_title_2", title=pr_info['title']))
    
    click.echo("\n" + "="*60)

